#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/printk.h>

// 定义模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luo");
MODULE_DESCRIPTION("A simple module for list");
MODULE_VERSION("1.0");

// 静态声明链表
static LIST_HEAD(mylist);

typedef struct my_data {
    int id;
    struct list_head plist;
}my_data;

// 初始化函数
static int __init mylist_init(void) {
    // 创建链表，创建5个节点
    struct my_data * tmp = NULL;
    printk(KERN_INFO "list: start initializing list\n");
    int _id = 0;
    for (_id = 0; _id < 5; ++_id) {
        // 创建一个数据节点
        // 上下文修饰符和区域修饰符
        tmp = kmalloc(sizeof(my_data), GFP_KERNEL | __GFP_ZERO);
        // 非空检测
        if (!tmp) {
            printk("list: fail to kmalloc\n");
            break;
        }
        // 成功创建，开始进行初始化
        tmp->id = _id;
        // 进行链表添加时，没有必要进行初始化
        list_add(&tmp->plist, &mylist);
        printk(KERN_INFO "list: Added node: id = %d\n", _id);
    }

    // 链表创建完毕，开始遍历链表
    
    struct list_head *pos = NULL;
    my_data *entry = NULL;

    // 使用list_for_each遍历
    printk(KERN_INFO "\nlist: list_for_each traversing\n");
    list_for_each(pos, &mylist) {
        entry = list_entry(pos, my_data, plist);
        printk(KERN_INFO "list: print1 node: id = %d\n", entry->id);
    }

    // 使用list_for_each_entry进行遍历
    printk(KERN_INFO "\nlist: list_for_each_entry traversing\n");
    list_for_each_entry(entry, &mylist, plist) {
        printk(KERN_INFO "list: print2 node: id = %d\n", entry->id);
    }

    // 使用list_for_each_entry_safe进行遍历，并删除节点
    my_data *nentry = NULL; 
    printk(KERN_INFO "\nlist: list_for_each_entry_safe traversing\n");
    list_for_each_entry_safe(entry, nentry, &mylist, plist) {
        pr_info("list: print3 node: id = %d\n", entry->id);
        // 删除当前节点
        list_del(&entry->plist);
        pr_info("list: delete a node\n");
        kfree(entry);
    }

    return 0;
}

// 销毁函数
static void __exit mylist_exit(void) {
    printk(KERN_INFO "list: list end\n");
    if (list_empty(&mylist)) {
        pr_info("list: list is now empty\n");
    }
}

module_init(mylist_init);
module_exit(mylist_exit);
